
<!doctype html>
<html lang="en">
<head>
	<title>A*寻路算法(threeJS)</title>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
	<link rel=stylesheet href="css/base.css"/>
	<style>
		#inset {
            width: 200px;
            height: 200px;
            background-color: #fff; /* or transparent; */
            border: 1px solid black; /* or none; */
            margin: 20px;
            padding: 0px;
            position: absolute;
            right: 0px;
            top: 0px;
            z-index: 100;
        }
	</style>
	
</head>
<body>
<!-- https://me.csdn.net/weixin_39028949  作者思路网址-->
<script src="js/three.js"></script>
<script src="js/Detector.js"></script>
<script src="js/Stats.js"></script>
<script src="js/OrbitControls.js"></script>
<script src="js/THREEx.KeyboardState.js"></script>
<script src="js/THREEx.FullScreen.js"></script>
<script src="js/THREEx.WindowResize.js"></script>
<script type="text/javascript" src="js/astar.js" ></script>
<script src="js/jquery-1.9.1.js"></script>
<script src="js/jquery-ui.js"></script>
<script type="text/javascript" src="js/CanvasRenderer.js" ></script>
<script type="text/javascript" src="js/Projector.js" ></script>
<link rel=stylesheet href="css/jquery-ui.css" />
<div id="ThreeJS" style="position: absolute; left:0px; top:0px"></div>
<!--<div id="inset"></div>-->
<script>

// MAIN

// standard global variables
var container, scene, camera, renderer, controls, stats,container2, scene2, camera2, renderer2;
//var keyboard = new THREEx.KeyboardState();

// custom global variables
var length=200;
var flex=2;

var track,curve,texture,tube;//路线对象
var progress=0;
var goFindWay=true;
var startPonit;
var navEndShow = false;
var navStatus = 'nav';//nav naving
var GoldPoint = {x:0,y:0,z:0};
var direction = 'up';
var pathlength = 0;
var circleP;


init();
//render();
animate();
// FUNCTIONS 		
function init() 
{
	// SCENE
	scene = new THREE.Scene();
	var graph = [];
	// CAMERA
	var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight;
	var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
	var raycaster;
	var CANVAS_WIDTH = 200;
    var CANVAS_HEIGHT = 200;
	camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
	scene.add(camera);
	camera.position.set(0,250,250);
	camera.lookAt(scene.position);	
	// RENDERER
	if ( Detector.webgl )
		renderer = new THREE.WebGLRenderer( {antialias:true} );
	else
		renderer = new THREE.CanvasRenderer(); 
	renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
	container = document.getElementById( 'ThreeJS' );
	container.appendChild( renderer.domElement );
	// EVENTS
	THREEx.WindowResize(renderer, camera);
	THREEx.FullScreen.bindKey({ charCode : 'm'.charCodeAt(0) });
	// CONTROLS
	controls = new THREE.OrbitControls( camera, renderer.domElement );
	console.log(96,controls);
	
	console.log(controls.getPolarAngle());
	console.log(controls.getAzimuthalAngle());
	
	controls.setAngle = function (phi, theta, distance) {
		var r = distance || controls.object.position.distanceTo(controls.target);
		var x = r * Math.cos(phi - Math.PI / 2) * Math.sin(theta) + controls.target.x;
		var y = r * Math.sin(phi + Math.PI / 2) + controls.target.y;
		var z = r * Math.cos(phi - Math.PI / 2) * Math.cos(theta) + controls.target.z;
		controls.object.position.set(x, y, z);
		controls.object.lookAt(controls.target);
    };
	
	//controls.setAngle(controls.getPolarAngle(),2);
    
    //controls.cameraSetTheta(2 * Math.PI/2);
	//controls.cameraSetTheta(2 * Math.PI);
	//controls.cameraSetTheta(-2 * Math.PI/2);
	//controls.cameraSetTheta(-Math.PI/2);
	


	// STATS
    // dom
    
	stats = new Stats();
	stats.domElement.style.position = 'absolute';
	stats.domElement.style.bottom = '0px';
	stats.domElement.style.zIndex = 100;
	container.appendChild( stats.domElement );
	// LIGHT
	var light = new THREE.PointLight(0xffffff);
	light.position.set(-100,200,100);
	scene.add(light);
	var ambientLight = new THREE.AmbientLight(0x111111);
	scene.add(ambientLight);
	initGround();
	function initGround(){
			// lines
		var geometry = new THREE.Geometry();
	    geometry.vertices.push( new THREE.Vector3( - length/2, 0, 0 ) );
	    geometry.vertices.push( new THREE.Vector3( length/2, 0, 0 ) );
	
	            for ( var i = 0; i <= length/10; i ++ ) {
	
	                var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x808080, opacity: 1 } ) );
	                line.position.z = ( i * 10 ) - length/2;
	                scene.add( line );
	
	                var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x808080, opacity: 1 } ) );
	                line.position.x = ( i * 10 ) - length/2;
	                line.rotation.y = 90 * Math.PI / 180;
	                scene.add( line );
	
	            }
	    var geometry = new THREE.PlaneGeometry( length, 10);
		var material = new THREE.MeshBasicMaterial( {color: 0x808080, side: THREE.DoubleSide} );
		var plane = new THREE.Mesh( geometry, material );
		plane.position.set(0,5,length/2);
		scene.add( plane );
		var plane = new THREE.Mesh( geometry, material );
		plane.rotation.y=90 * Math.PI / 180;
		plane.position.set(length/2,5,0);
		scene.add( plane );	
		var plane = new THREE.Mesh( geometry, material );
		plane.position.set(0,5,-length/2);
		scene.add( plane );
		var plane = new THREE.Mesh( geometry, material );
		plane.rotation.y=90 * Math.PI / 180;
		plane.position.set(-length/2,5,0);
		scene.add( plane );
		
		var skyBoxGeometry = new THREE.CubeGeometry( 10000, 10000, 10000 );
		var skyBoxMaterial = new THREE.MeshBasicMaterial( { color: 0xFFFFFF, side: THREE.BackSide } );
		var skyBox = new THREE.Mesh( skyBoxGeometry, skyBoxMaterial );
		scene.add(skyBox);
		
		scene.fog = new THREE.FogExp2( 0xFFFFFF, 0.00025 );
		
		createjiantou();
	
	}
	initGrid();
	function initGrid(){
		var geometry = new THREE.CubeGeometry( 8,8,8);
		var material = new THREE.MeshBasicMaterial( {color: 0xC0C0C0} );
		for(var i=0;i<length/10;i++){
			 var nodeRow = [];
             graph.push(nodeRow);
			for(var j=0;j<length/10;j++){
				var salt=randomNum(1,7);
				if(salt>flex){
					nodeRow.push(1);
				}else{
					nodeRow.push(0);
				}
				if(salt<=flex){					
					var cube = new THREE.Mesh( geometry, material );
					cube.position.set(10*j-length/2+5,5,10*i-length/2+5);
					scene.add(cube);
				}
			}
		}
		console.log(graph);
	}
	
//	container2 = document.getElementById('inset');
//  // renderer
//  renderer2 = new THREE.CanvasRenderer();
//  renderer2.setSize(CANVAS_WIDTH, CANVAS_HEIGHT);
//  container2.appendChild(renderer2.domElement);
//  // scene
//  scene2 = new THREE.Scene();
//  // camera
//  camera2 = new THREE.PerspectiveCamera(90, CANVAS_WIDTH / CANVAS_HEIGHT, 1, 1000);
//  camera2.position.set(0,200,0)
//  camera2.up=camera.up;
//  camera2.lookAt(0,1,10);
//  scene2.add(camera2);
//  var dir = new THREE.Vector3( 20, 20, 20 );
//  var origin = new THREE.Vector3( 0, 0, 0 );
//  var length = 50;
//  var hex = 0x000000;
//  var arrowHelper = new THREE.ArrowHelper( dir.normalize(), origin, length, hex );
//  scene2.add( arrowHelper );
//
//  // axes
//  axes2 = new THREE.AxisHelper(200);
//  scene2.add(axes2);
	
    
    var reslutArray=new Array();
	raycaster = new THREE.Raycaster();
	let mouse = new THREE.Vector2();
	function pickupObjects(e){
	         //将鼠标点击位置的屏幕坐标转成threejs中的标准坐标
	          var fxl=new THREE.Vector3(0,1,0); 
	          var groundplane=new THREE.Plane(fxl,0);
	          mouse.x = (e.clientX/window.innerWidth)*2 -1;
	          mouse.y = -(e.clientY/window.innerHeight)*2 + 1;
	          //从相机发射一条射线，经过鼠标点击位置
	          raycaster.setFromCamera(mouse,camera);
	          //计算射线相机到的对象，可能有多个对象，因此返回的是一个数组，按离相机远近排列
	          var ray=raycaster.ray;
	          let intersects = ray.intersectPlane(groundplane);
	          let x=intersects.x;
	          let z=intersects.z;
	          //console.log(x+" "+z);
	          if(Math.abs(x)>length/2||Math.abs(z)>(length/2)){
	          	return;
	          }  
	          /* 
	          if((Math.abs(x)<=((length-20)/2)&&Math.abs(z)<=(length-20)/2)){
	          	alert("请在边缘位置选择开始或结束点");
	          	return;
	          } 
	          */ 
	          var k,m;
                    for(var i=-length/2;i<length/2;i+=10){
                    	if(x>=i&&x<i+10){
                    		k=i+5;
                    	}
                    }
                    for(var j=-length/2;j<length/2;j+=10){
                    	if(z>=j&&z<j+10){
                    		m=j+5;
                    	}
                    }
              console.log(k+' '+m);
              initSphere(k,m);
	          
	}
	document.addEventListener('click',pickupObjects,false);
	var isCaculate=false;
	function cleanSphere(){
		let child=scene.children;
            for(var i=0;i<child.length;i++){
            	if(child[i].geometry instanceof THREE.SphereGeometry){
            		scene.remove(child[i]);
            		i--;
            	}
            }
			isCaculate=false;
	}
	function initSphere(x,z){
		
		if (navStatus == "naving") {
			return;
		}
		if(isCaculate){
			cleanSphere();  
		}
		if(reslutArray.length==0){
            //清除路径
			navEndShow = false;
			console.log('清除路径');
			scene.remove(track);
			scene.remove(tube);
			scene.remove(circleP);
			progress=0;
			curve=null;
			
			
			
			goFindWay = false;
			var geometry = new THREE.SphereGeometry( 5, 32, 32 );
			var material = new THREE.MeshBasicMaterial( {color: 0xffff00} );
			var sphere = new THREE.Mesh( geometry, material );
			sphere.position.x=x;
			sphere.position.y=5;
			sphere.position.z=z;
			sphere.name="sphere";
			
			if (confirm("确定设置为起点？")){
			   reslutArray.push(sphere);
			   scene.add( sphere );
			}
			//不能选择直线
		//} else if(reslutArray[0].position.x!=x&&reslutArray[0].position.z!=z){
	    } else {
			
			var geometry = new THREE.SphereGeometry( 5, 32, 32 );
			var material = new THREE.MeshBasicMaterial( {color: 0xffff00} );
			var sphere = new THREE.Mesh( geometry, material );
			sphere.position.x=x;
			sphere.position.y=5;
			sphere.position.z=z;
			sphere.name="sphere1";
			if (confirm("确定设置为终点？")){
			   reslutArray.push(sphere);
			   scene.add( sphere );
			}
			
			caculatePath(reslutArray);
			
			isCaculate = true;

			
			console.log(303,circleP,startPonit);
	
			
			goFindWay = false;
			
			
			//alert('准备出发');
			
			circleP.position.set(startPonit.x, 2, startPonit.z);
		    scene.add(circleP);
			//调整箭头,旋转视角
			//initDirection(reslutArray[0],reslutArray[1]);
			
			//getDirection(reslutArray[1]);
			
			setTimeout(function(){
				goFindWay = true;
				
			},5000)
			
			navStatus = "naving";
			reslutArray = new Array();
		}
		//console.log(reslutArray);
		
	}
	
	function caculatePath(resultArray){
		
		//console.log(resultArray);
		var maps = new Graph(graph);
		var startX=(resultArray[0].position.z-5+length/2)/10;
		var startY=(resultArray[0].position.x-5+length/2)/10;
		var endX=(resultArray[1].position.z-5+length/2)/10;
		var endY=(resultArray[1].position.x-5+length/2)/10;
		console.log(309,startX+' '+startY+' '+endX+' '+endY);
	    var start = maps.grid[startX][startY];
	    var end = maps.grid[endX][endY];
		console.log(326,start);
	    var result = astar.search(maps, start, end);
		pathlength = result.length;
	    if(result.length==0){
	    	alert("无可到达路径");
	    	cleanSphere(); 
	    	return;
	    }
		console.log(332,result);
		
		var arr = [];
		
		for(var i in result) {
			arr.push(new THREE.Vector3(result[i].y*10-100, 0, result[i].x*10-100));
		}
		startPonit=arr[0];
		GoldPoint = arr[0];

		
		
		/**
		 * 创建一个设置重复纹理的管道
		 */
		console.log(363,arr);
		curve = new THREE.CatmullRomCurve3(arr,false/*是否闭合*/);
	    console.log(365,curve);
		
		createjiantou(curve);
		
		var tubeGeometry = new THREE.TubeGeometry(curve, 100, 1.5, 50, false);
		var tubeMaterial = new THREE.MeshPhongMaterial({
		  color: 0x25b115,
		  transparent: true,
		  opacity: 1,
		});
		tube = new THREE.Mesh(tubeGeometry, tubeMaterial);
		scene.add(tube)
		
	   
		/**
		 * 创建一个半透明管道  
		 */
		var textureLoader = new THREE.TextureLoader();
		texture = textureLoader.load('./img/a1.png');
		texture.wrapS = THREE.RepeatWrapping
		texture.wrapT= THREE.RepeatWrapping
		texture.repeat.x = 20;
		
		var tubeGeometry2 = new THREE.TubeGeometry(curve, 100, 1.5, 50, false);
		var tubeMaterial2 = new THREE.MeshPhongMaterial({
		  map: texture,
		  transparent: true,
		  opacity: 1,
		});
		track = new THREE.Mesh(tubeGeometry2, tubeMaterial2);
		scene.add(track);
	}
	
	function createjiantou(curve){
		direction = 'up';
		console.log(396111111111111111);
		var textureLoader = new THREE.TextureLoader();
		texture1 = textureLoader.load('./img/arrow.png');
		texture1.wrapS = THREE.RepeatWrapping
		texture1.wrapT= THREE.RepeatWrapping
		
		//geometryP = new THREE.PlaneGeometry(10,10,1,1);
		geometryP = new THREE.CircleGeometry(5, 1000);
		
		//geometryP.rotateZ(-Math.PI/2);
	    //geometryP = new THREE.SphereGeometry(5,16,16); 
	    //var materialP = new THREE.MeshBasicMaterial( { color: 0xff0000 ,side:THREE.DoubleSide} );
		
		var materialP = new THREE.MeshPhongMaterial({
		  map: texture1
		});
		
	    circleP = new THREE.Mesh( geometryP, materialP );
		circleP.rotation.set(-0.5 * Math.PI, 0, 0); // 沿着 X轴旋转-90°
		if (curve)  {
		  getDirection(curve.getPoint(0),curve.getPoint(0.02),true);
		}
		//circleP.rotateZ(-Math.PI); // 沿着 z轴旋转-90°
		
        //circleP.position.set(30, 30, 30);
	    //scene.add( circleP );
	    //geometryP.rotateY(Math.PI/2);
	    //console.log('createjiantou');
	    //circleP.position.set(0, 0, 0);
	}
	
	function randomNum(minNum,maxNum){ 
		    switch(arguments.length){ 
		        case 1: 
		            return parseInt(Math.random()*minNum+1,10); 
		        break; 
		        case 2: 
		            return parseInt(Math.random()*(maxNum-minNum+1)+minNum,10); 
		        break; 
		            default: 
		                return 0; 
		            break; 
		    } 
		} 

	
}

function animate() 
{
	//console.log('render');
    requestAnimationFrame( animate );
	renderer.render( scene, camera );	
//	renderer2.render(scene, camera2);
//	camera2.position.set(0,200,0)
//  camera2.up=camera.up;
//  camera2.lookAt(0,1,0);
	//camera2.position.copy( camera.position );
//  camera2.position.setLength( 300 );
    //camera2.lookAt( scene2.position );
	controls.update();
	stats.update();
	if(texture && goFindWay) {
	  texture.offset.x -= 0.02;
	  if(progress > 1.0) {
		  if(navEndShow == false) {
			navEndShow = true;
			navStatus = "nav";
		    alert("导航结束！");
		  }
		  return;    //停留在管道末端,否则会一直跑到起点 循环再跑
	  }
	  
	  //progress += 0.002;  
	  progress += 0.002 / Math.ceil(pathlength/10);  
	
	  /*
	  if (pathlength > 0) {
		progress += (0.002 * 210 / pathlength);
	  } else {
		progress += 0.002;  
	  }
	  */
	  //console.log(480,pathlength,progress,(0.002 * 210 / pathlength));
	  //console.log(progress);
	  if(curve){
		  let point = curve.getPoint(progress);
		  if(point&&point.x){
			console.log(413,point);
			getDirection(GoldPoint,point);
		    circleP.position.set(point.x,2,point.z);
		  }
	  }
	}   
}



function getDirection(oldPoint,point,type) {
	
	let x = Math.abs(point.x - oldPoint.x);
	let z = Math.abs(point.z - oldPoint.z);
	console.log(538,oldPoint,point);
	console.log(539,direction,x > z,point.x - oldPoint.x,controls.getAzimuthalAngle());
	
	if (x > z && (point.x - oldPoint.x) > 0.01) {
		if (direction == "left") {
		    //顺时针 180度
		    circleP.rotateZ(Math.PI);
		    this.cameraRotate(controls.getAzimuthalAngle(),Math.PI);
		}
		if (direction == 'down') {
			console.log('aaaaaaaa',controls.getAzimuthalAngle());
            //逆时针 90度
            circleP.rotateZ(Math.PI/2);
            this.cameraRotate(controls.getAzimuthalAngle(),Math.PI/2);
		}
		if (direction == 'up') {
			//逆时针 90度
		   circleP.rotateZ(-Math.PI/2);
		   this.cameraRotate(controls.getAzimuthalAngle(),-Math.PI/2);
		}
		direction = 'right';
	} else if (x > z && (point.x - oldPoint.x) < -0.01) {
		if (direction == "right") {
		    //顺时针 180度
		   circleP.rotateZ(Math.PI);
		   this.cameraRotate(controls.getAzimuthalAngle(),Math.PI);
		}
		if (direction == 'down') {
		   //顺时针 90度
		   circleP.rotateZ(Math.PI/2);
		   this.cameraRotate(controls.getAzimuthalAngle(),Math.PI/2);
		}
		if (direction == 'up') {
			//顺时针 90度
		   circleP.rotateZ(Math.PI/2);
		   this.cameraRotate(controls.getAzimuthalAngle(),Math.PI/2);
		}
		direction = 'left';
	} else if (x < z && (point.z - oldPoint.z) > 0.01) {
		if (direction == "up") {
			//顺时针 180度
		    circleP.rotateZ(Math.PI);
			console.log(579,controls.getAzimuthalAngle(),Math.PI);
			this.cameraRotate(controls.getAzimuthalAngle(),Math.PI);
		}
		if (direction == 'right') {
			//顺时针 90度
			circleP.rotateZ(Math.PI/2);
			this.cameraRotate(controls.getAzimuthalAngle(),Math.PI/2);
		}
		if (direction == 'left') {
			//逆时针 90度
			circleP.rotateZ(-Math.PI/2);
			this.cameraRotate(controls.getAzimuthalAngle(),-Math.PI/2);
		}
		direction = 'down';
	} else if (x < z && (point.z - oldPoint.z) < -0.01) {
		if (direction == "down") {
		   //顺时针 180度
		   circleP.rotateZ(Math.PI);
		   this.cameraRotate(controls.getAzimuthalAngle(),Math.PI);
		}
		if (direction == 'right') {
           //逆时针 90度
           circleP.rotateZ(-Math.PI/2);
           this.cameraRotate(controls.getAzimuthalAngle(),-Math.PI/2);
		}
		if (direction == 'left') {
           //顺时针 90度
           circleP.rotateZ(Math.PI/2);
           this.cameraRotate(controls.getAzimuthalAngle(),Math.PI/2);
		}
		direction = 'up';
	}
	if (type) {
	  GoldPoint = oldPoint;
	} else {
	  GoldPoint = point;
	}
	return direction;
}


function cameraRotate(a,b) {
	if ( a > b) {
		jian(a,b);
	} else if (a < b) {
		jia(a,b);
	}  
}

function jian(a,b) {
	console.log('jian',a,b);
	if (a >= b) {
		controls.cameraSetTheta(-0.02);
		setTimeout(function() {
			jian(controls.getAzimuthalAngle(),b);
		},0);
    } else {
		controls.cameraSetTheta(0);
	}
}

function jia(a,b) {
	console.log('jia',a,b);
	if (a <= b) {
		controls.cameraSetTheta(0.02);
		setTimeout(function() {
			jia(controls.getAzimuthalAngle(),b);
		},0);
    } else {
		controls.cameraSetTheta(0);
	}
}





</script>

</body>
</html>